#include "LecteurPhraseAvecArbre.h"

#include <stdlib.h>
#include <iostream>
using namespace std;

////////////////////////////////////////////////////////////////////////////////

LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
ls(nomFich), ts() {
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::analyse() {
    arbre = programme();
    cout << "Syntaxe correcte." << endl;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::programme() {
    // <programme> ::= debut <seq_inst> fin FIN_FICHIER

    sauterSymCour("debut");
    Noeud* si = seqInst();
    sauterSymCour("fin");
    testerSymCour("<FINDEFICHIER>");
    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::seqInst() {
    // <seqInst> ::= <inst> ; { <inst> ; }

    NoeudSeqInst* si = new NoeudSeqInst();
    do {
        si->ajouteInstruction(inst());
        sauterSymCour(";");
    } while (ls.getSymCour() == "<VARIABLE>" ||
            (ls.getSymCour() == "si") ||
            (ls.getSymCour() == "tantque") ||
            (ls.getSymCour() == "repeter") ||
            (ls.getSymCour() == "pour") ||
            (ls.getSymCour() == "lire") ||
            (ls.getSymCour() == "ecrire")
            );
    // tant que le symbole courant est un debut possible d'instruction...
    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::inst() {
    // <inst> ::= <affectation> | <inst_condi>
    if (ls.getSymCour() == "<VARIABLE>")
        return affectation();
    else if (ls.getSymCour() == "si")
        return instSi();
    else if (ls.getSymCour() == "tantque")
        return instTq();
    else if (ls.getSymCour() == "repeter")
        return instRepeter();
    else if (ls.getSymCour() == "lire")
        return instLire();
    else if (ls.getSymCour() == "ecrire")
        return instEcrire();
    else if (ls.getSymCour() == "pour")
        return instPour();
    else
        erreur("<inst>");
    return NULL;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::affectation() {
    // <affectation> ::= <variable> = <expression>

    testerSymCour("<VARIABLE>");
    Noeud* var = ts.chercheAjoute(ls.getSymCour());
    ls.suivant();
    sauterSymCour("=");
    Noeud* exp = expression();
    return new NoeudAffectation(var, exp);
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expression() {
    // <experssion> ::= <terme> { <opAdd> <terme> }


    Noeud* term = terme();
    while (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
        Symbole operateur = opAdd(); // on stocke le symbole de l'opérateur
        Noeud* termDroit = terme(); // lecture de l'operande droit
        term = new NoeudOperateurBinaire(operateur, term, termDroit); // const. du noeud
    }
    return term;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::terme() {
    // <terme> ::= <facteur> { <opMulti> <facteur> }

    Noeud* fact = facteur();
    while (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        Symbole operateur = opMult();
        Noeud* factDroit = facteur();
        fact = new NoeudOperateurBinaire(operateur, fact, factDroit);
    }
    return fact;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::facteur() {
    // <facteur> ::= <entier>  |  <variable>  |  - <facteur>  |  ( <expression> )
    // <facteur> ::= <entier> | <variable> | <opUnaire> <expBool> | (<expBool> )

    Noeud* fact = NULL;
    if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {
        // on représente le moins unaire (- facteur) par une soustractin binaire (0 - facteur)
        fact = new NoeudOperateurBinaire(opUnaire(), ts.chercheAjoute(Symbole("0")), expBool());

    } else if (ls.getSymCour() == "<VARIABLE>" || ls.getSymCour() == "<ENTIER>"|| ls.getSymCour() =="<CHAINE>") {
        fact = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
    } else if (ls.getSymCour() == "(") {
        ls.suivant();
        fact = expBool();
        sauterSymCour(")");
    } else
        erreur("<facteur>");
    return fact;
}


////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::termeBool() {
    // <termeBool> ::= <relation> {<opEt> <relation>}
    Noeud* rela = NULL;

    rela = relation();
    while (ls.getSymCour() == "et") {
        Symbole operateur = opEt();
        Noeud* relaDroit = relation();
        rela = new NoeudOperateurBinaire(operateur, rela, relaDroit);
    }

    return rela;
}
//////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expBool() {
    // <expBool> ::= <termeBool> { <opOu> <termeBool> }
    Noeud* term = NULL;

    term = termeBool();
    while (ls.getSymCour() == "ou") {
        Symbole operateur = opOu();
        Noeud* termDroit = termeBool();
        term = new NoeudOperateurBinaire(operateur, term, termDroit);
    }

    return term;
}
//////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::relation() {
    // <relation> ::= <expression> { <opRel> <expression> }
    Noeud* exp = NULL;
    exp = expression();
    while (ls.getSymCour() == "=="
            || ls.getSymCour() == "!="
            || ls.getSymCour() == "<"
            || ls.getSymCour() == "<="
            || ls.getSymCour() == ">"
            || ls.getSymCour() == ">=") {
        Symbole operateur = opRel(); // on stocke le symbole de l'opérateur
        Noeud* expDroit = expression(); // lecture de l'operande droit
        exp = new NoeudOperateurBinaire(operateur, exp, expDroit); // const. du noeud
    }
    return exp;
}
//////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opRel() {
    // <opRel> ::= == | != | < | <= | > | >=
    Symbole operateur;

    if (ls.getSymCour() == "==" ||
            ls.getSymCour() == "!=" ||
            ls.getSymCour() == "<" ||
            ls.getSymCour() == "<=" ||
            ls.getSymCour() == ">" ||
            ls.getSymCour() == ">=") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opRel>");

    return operateur;
}
//////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opUnaire() {
    // <opUnaire> ::= - | non
    Symbole operateur;

    if (ls.getSymCour() == "-" ||
            ls.getSymCour() == "non") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opUnaire>");

    return operateur;
}


//////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opAdd() {
    // <opBinaire> ::= + | -
    Symbole operateur;
    if (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opAdd>");
    return operateur;
}
////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opMult() {
    // <opBinaire> ::= * | /
    Symbole operateur;
    if (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opMult>");
    return operateur;
}
////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opEt() {
    // <opEt> ::= et
    Symbole operateur;
    if (ls.getSymCour() == "et") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opEt>");
    return operateur;
}
////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opOu() {
    // <opOu> ::= ou
    Symbole operateur;
    if (ls.getSymCour() == "ou") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opOu>");
    return operateur;
}
///////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instSi() {
    //<instSi> ::= si (<expBool> ) <seqInst> { sinonsi ( <expBool>) <seqInst> } [ sinon <seqInst> ] finsi
    // (sous forme de liste chainée)
    Noeud* expB = NULL;
    Noeud* seqI = NULL;
    NoeudInstSi* deb = NULL;
    NoeudInstSi* si1 = NULL;
    NoeudInstSi* si2 = NULL;


    sauterSymCour("si");
    sauterSymCour("(");
    expB = expBool();
    sauterSymCour(")");
    seqI = seqInst();
    deb = new NoeudInstSi(expB, seqI, si2, NoeudInstSi::SI);
    si1 = deb;
    while (ls.getSymCour() == "sinonsi") {
        ls.suivant();
        sauterSymCour("(");
        expB = expBool();
        sauterSymCour(")");
        seqI = seqInst();
        si2 = new NoeudInstSi(expB, seqI, NULL, NoeudInstSi::SINONSI);
        si1->setInstSi(si2);
        si1 = si2;
    }
    if (ls.getSymCour() == "sinon") {
        ls.suivant();
        seqI = seqInst();
        si2 = new NoeudInstSi(ts.chercheAjoute(Symbole("1")), seqI, NULL, NoeudInstSi::SINON);
        si1->setInstSi(si2);
        si1 = si2;
    }
    sauterSymCour("finsi");
    return deb;
}
/////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instTq() {
    // <instTq> ::= tantque ( <expBool> ) <seqInst> fintantque

    Noeud* Tq = NULL;


    sauterSymCour("tantque");
    sauterSymCour("(");
    Noeud* expB = expBool();
    sauterSymCour(")");
    Noeud* seqI = seqInst();
    sauterSymCour("fintantque");
    Tq = new NoeudTq(expB, seqI);


    return Tq;
}

/////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instRepeter() {
    // <instRepeter> ::= repeter <seqInst> jusqua (<expBool>)
    Noeud* instRep = NULL;


    sauterSymCour("repeter");
    Noeud* seqI = seqInst();
    sauterSymCour("jusqua");
    sauterSymCour("(");
    Noeud* expB = expBool();
    sauterSymCour(")");
    instRep = new NoeudRep(expB, seqI);


    return instRep;
}
///////////////////////////////////////////////////////////////////////// 

Noeud* LecteurPhraseAvecArbre::instPour() {
    // <instPour> ::= pour (<affectation>;<expBool>;<affectation>) <seqInst> finpour
    Noeud* instPr = NULL;


    sauterSymCour("pour");
    sauterSymCour("(");
    Noeud* aff1 = affectation();
    sauterSymCour(";");
    Noeud* expB = expBool();
    sauterSymCour(";");
    Noeud* aff2 = affectation();
    sauterSymCour(")");
    Noeud* seqI = seqInst();
    sauterSymCour("finpour");
    instPr = new NoeudPr(aff1, expB, aff2, seqI);


    return instPr;
}
/////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instLire() {
    //<instLire> ::= lire ( <variable> )
    Noeud* fact = NULL;


    sauterSymCour("lire");
    sauterSymCour("(");
    if (ls.getSymCour() == "<VARIABLE>") {
        fact = facteur();
        sauterSymCour(")");
    } else
        erreur("<variable>");


    return new NoeudLire(fact);
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instEcrire() // <instEcrire> ::= ecrire ( <expression> | <chaine> )
{
    Noeud * param = NULL;

    sauterSymCour("ecrire");
    sauterSymCour("(");
    if (ls.getSymCour() == "<CHAINE>") {
        param = facteur();
    } else
        param = expression();
    sauterSymCour(")");


    return new NoeudEcrire(param);
}


/////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::testerSymCour(string ch) {
    if (ls.getSymCour() != ch) {
        cout << endl << "-------- Erreur ligne " << ls.getLigne()
                << " - Colonne " << ls.getColonne() << endl << "   Attendu : "
                << ch << endl << "   Trouve  : " << ls.getSymCour() << endl
                << endl;
        exit(0); // plus tard, on levera une exception
    }
}
////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
    testerSymCour(ch);
    ls.suivant();
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::erreur(string mess) {
    cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
            << ls.getColonne() << endl << "   Attendu : " << mess << endl
            << "   Trouve  : " << ls.getSymCour() << endl << endl;
    exit(0); // plus tard, on levera une exception
}
